import commonMixin from '../mixins/common.js'

import CM from '@/class/index.js'
import utils from '@/utils/index.js'

// 滑动动画默认持续时间
const ANI_DURATION = 150

export default {
  name: 'cm-calendar',
  mixins: [
    commonMixin
  ],
  props: {
    /* 
      初始化的日期的设定
      可以用于初始化CMDate对象即可
    */
    dateInit: {
      type: Object,
      default: null
    },
    // 是显示周历还是月历
    type: {
      default: 'month',
      type: String,
      validator (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['month', 'week'].indexOf(value) !== -1
      }
    },
    // 动画持续时间
    aniDuration: {
      default: ANI_DURATION,
      type: Number
    }
  },
  data () {
    return {
      // 选中的日期
      date: null,
      // 当前swiper真正的索引
      curIndex: 1,
      // 控制swiper动画的索引
      swiperIndex: 1,
      // 手动控制轮播滑动的锁
      swiperLock: false,
      // 月历列表，0 1 2 分别对应三个swiper-item
      dateSet: [[], [], []],
    }
  },
  computed: {
    // 选中的日期
    selectedIndex () {
      if (!this.date) { return -1 }
      const index = this.dateSet[this.curIndex].findIndex(item => item.rulue === this.date.rulue)
      return index
    },
    
    // 前一个swiper索引
    prevIndex () {
      if (this.curIndex === 0) {
        return 2
      }
      return this.curIndex - 1
    },
    
    // 后一个swiper索引
    nextIndex () {
      if (this.curIndex === 2) {
        return 0
      }
      return this.curIndex + 1
    },
    
    // 当前月
    curMonth () {
      if (!this.date) { return {} }
      return {
        year: this.date.year,
        month: this.date.month
      }
    },
    
    // 前一个月
    prevMonth () {
      if (typeof this.curMonth.year === 'number' && typeof this.curMonth.month === 'number') {
        return CM.Date.getDesMonth(this.curMonth.year, this.curMonth.month, -1)
      }
      return {}
    },
    
    // 后一个月
    nextMonth () {
      if (typeof this.curMonth.year === 'number' && typeof this.curMonth.month === 'number') {
        return CM.Date.getDesMonth(this.curMonth.year, this.curMonth.month, 1)
      }
      return {}
    }
  },
  methods: {
    // 初始化，isEventActive指定事件是否要触发
    init (date, isEventActive = true) {
      // 初始化选中日期
      this.date = new CM.Date(date)
      if (!this.date.enabled) {
        // 初始化的日期不合法，用当前的时间
        this.date = new CM.Date()
      }
      
      // 初始化日历数据
      this.dataSet = [[], [], []]
      const data = this.type === 'month' ?
        CM.Date.getCalendarMonthData(this.curMonth.year, this.curMonth.month) :  
        CM.Date.getCalendarWeekData(this.curMonth.year, this.curMonth.month, this.date.day)   
      this.$set(this.dateSet, this.prevIndex, data[0])
      this.$set(this.dateSet, this.curIndex, data[1])
      this.$set(this.dateSet, this.nextIndex, data[2])    
      
      // 初始化完成，触发一次事件
      if (isEventActive) {
        this.onDateChange()
      }
    },
    
    // 选中日期发生变动时触发
    onDateChange () {
      this.$emit('date-change', this.date, this.type)
    },
    
    // 设置时间
    gotoTime (hour, min) {
      const options = {
        year: this.date.year,
        month: this.date.month,
        day: this.date.day,
        hour, min
      }
      const newDate = new CM.Date(options)
      if (!newDate.enabled) {
        // 目标日期不合法，报错
        uni.showToast({
          title: '不合法的日期',
          icon: 'none'
        })
        return
      }
      // 时间更新
      this.date = newDate
    },
    
    // 点击日期触发
    dayClickHandler: utils.common.throttle(function (day) {
      this.gotoDay(day.year, day.month, day.day)
    }, ANI_DURATION),
    
    // 选择某日
    gotoDay (year, month, day) {
      // 锁开启
      this.swiperLock = true
      
      const options = {
        year, month, day,
        hour: this.date.hour,
        min: this.date.min
      }
      const newDate = new CM.Date(options)
      if (!newDate.enabled) {
        // 目标日期不合法，报错
        uni.showToast({
          title: '不合法的日期',
          icon: 'none'
        })
        return
      }
      
      if (this.type === 'month') {
        // 调整月历数据
        this.gotoDayByMonth(year, month, day, newDate, () => {
          // 所有事情完成后，锁关闭
          this.swiperLock = false
          // 触发日期更新
          this.onDateChange()
        })
      }
      else {
        this.gotoDayByWeek(year, month, day, newDate, () => {
          // 所有事情完成后，锁关闭
          this.swiperLock = false
          // 触发日期更新事件
          this.onDateChange()
        })
      }
    },
    
    // 按月跳转到对应的日期，并调整月历数据
    gotoDayByMonth (year, month, day, newDate, callback = () => {}) {      
      if (year > this.date.year || (year === this.date.year && month > this.date.month)) {
        // 选择的日期在当月之后的月份
        // 先设置下一个月的数据
        const nextData = CM.Date.getCalendarMonth(year, month)
        this.$set(this.dateSet, this.nextIndex, nextData)
        // 日期跟进
        this.date = newDate
        // 移动到下一个月，并触发事件
        this.swipe(1, (index) => {
          // 索引跟进
          this.curIndex = index
          // 回调函数中，把先前的一个月的数据重置
          const prevData = CM.Date.getCalendarMonth(this.prevMonth.year, this.prevMonth.month)
          this.$set(this.dateSet, this.prevIndex, prevData)
          // 把之后的一个月的数据重置
          const nextData = CM.Date.getCalendarMonth(this.nextMonth.year, this.nextMonth.month)
          this.$set(this.dateSet, this.nextIndex, nextData)
          // 所有事情完成后，执行回调
          callback()
        })
      }
      else if (year < this.date.year || (year === this.date.year && month < this.date.month)) {
        // 选择的日期在当月之前的月份
        // 先设置上一个月的数据
        const prevData = CM.Date.getCalendarMonth(year, month)
        this.$set(this.dateSet, this.prevIndex, prevData)
        // 日期跟进
        this.date = newDate
        // 移动到上一个月，并触发事件
        this.swipe(-1, (index) => {
          // 索引跟进
          this.curIndex = index
          // 回调函数中，把先前的一个月的数据重置
          const prevData = CM.Date.getCalendarMonth(this.prevMonth.year, this.prevMonth.month)
          this.$set(this.dateSet, this.prevIndex, prevData)
          // 把之后的一个月的数据重置
          const nextData = CM.Date.getCalendarMonth(this.nextMonth.year, this.nextMonth.month)
          this.$set(this.dateSet, this.nextIndex, nextData)
          // 所有事情完成后，执行回调
          callback()
        })
      }
      else {
        // 选择的日期在当月，只需要跟进事件
        this.date = newDate
        // 执行回调
        callback()
      }
    },
    
    // 按周跳转到对应的日期，并调整月历数据
    gotoDayByWeek (year, month, day, newDate, callback = () => {}) {
      const weekInDay = CM.Date.config.DAY_IN_WEEK 
      // 保存变更前的日期
      const tempThisWeek = {
        start: this.dateSet[this.curIndex][0].rulue,
        end: this.dateSet[this.curIndex][weekInDay - 1].rulue
      }
      if (newDate.rulue > tempThisWeek.end) {
        // 选择的日期在当周之后
        // 先设置下一个周的数据
        const nextData = CM.Date.getCalendarWeek(year, month, day)
        this.$set(this.dateSet, this.nextIndex, nextData)
        // 日起跟进
        this.date = newDate
        console.log('日期变更')
        // 移动到下一个周，并触发事件
        this.swipe(1, (index) => {
          // 索引跟进
          this.curIndex = index
          // 回调函数中，把先前的一个周的数据重置
          const prevData = CM.Date.getCalendarWeek(year, month, day, -1)
          this.$set(this.dateSet, this.prevIndex, prevData)
          // 回调函数中，把之后的一个周的数据重置
          const nextData = CM.Date.getCalendarWeek(year, month, day, 1)
          this.$set(this.dateSet, this.nextIndex, nextData)
          // 所有事情完成后，执行回调
          callback()
        })
      }
      else if (newDate.rulue < tempThisWeek.start) {
        // 选择的日期在当周之前
        // 先把上一个周的数据重置了
        const prevData = CM.Date.getCalendarWeek(year, month, day)
        this.$set(this.dateSet, this.prevIndex, prevData)
        // 日起跟进
        this.date = newDate
        // 移动到前一个周，并触发事件
        this.swipe(-1, (index) => {
          // 索引跟进
          this.curIndex = index
          // 回调函数中，把先前的一个周的数据重置
          const prevData = CM.Date.getCalendarWeek(year, month, day, -1)
          this.$set(this.dateSet, this.prevIndex, prevData)
          // 回调函数中，把之后的一个周的数据重置
          const nextData = CM.Date.getCalendarWeek(year, month, day, 1)
          this.$set(this.dateSet, this.nextIndex, nextData)
          // 所有事情完成后，执行回调
          callback()
        })
      }
      else {
        this.date = newDate
        // 执行回调
        callback()
      }
    },
    
    // 手势滑动触发事件
    swipeHandler (e) {
      if (this.swiperLock) {
        // 当手操滑动对象时，不允许触发自动事件
        return
      }
      console.log(e)
      
      const index = e.detail.current
      let sign = 'prev'
      if (index === this.nextIndex) {
        sign = 'next'
      }
      
      // 索引跟进
      this.curIndex = index
      // swiper控制器跟进
      this.swiperIndex = index
         
      console.log('开始延时', this.aniDuration)
      const a = new Date().getTime()
      console.time('延时时间')
      clearTimeout(this.swiperHandlerTimer)
      this.swiperHandlerTimer = setTimeout(() => {
        console.log('log版本延时时间：', new Date().getTime() - a)
        console.timeEnd('延时时间')
        // 更新轮播图数据
        this.updateSwiper(sign)
      }, this.aniDuration)
      
      /*
      // #ifdef MP
      // 小程序端setTimeout有严重BUG，先如此处理
      this.updateSwiper(sign)
      // #endif
      */
    },
    
    // swiper滑动时更新数据，sign指定了向前还是向后
    updateSwiper (sign = 'next') {
      // 动画完成后，再跟进日期和数据
      if (this.swiperLock) {
        // 双保险,当手操滑动对象时，不允许更新数据
        return
      }
      // 日期跟进
      if (this.type === 'month') {
        // 月日期跟进
        const tempDate = this.dateSet[this.curIndex][CM.Date.config.DAY_IN_WEEK]
        let options = {
          year: tempDate.year,
          month: tempDate.month,
          day: this.date.day,
          hour: this.date.hour,
          min: this.date.min
        }
        let newDate = new CM.Date(options)
        while (!newDate.enabled && options.day > 0) {
          // 遇上无效的，不存在的日期，要向前追溯
          options.day--
          newDate = new CM.Date(options)
        }
        this.date = newDate            
      }
      else {
        // 周日期跟进
        const weekInDay = CM.Date.config.DAY_IN_WEEK
        const startRulue = this.dateSet[this.curIndex][0].rulue
        const desRulue = startRulue + this.date.weekDay
        let options = {
          rulue: desRulue,
          hour: this.date.hour,
          min: this.date.min
        }       
        this.date = new CM.Date(options)           
      }
      
      
      // 数据跟进
      if (sign === 'next') {
        const data = this.type === 'month' ?
          CM.Date.getCalendarMonth(this.nextMonth.year, this.nextMonth.month) :
          CM.Date.getCalendarWeek(this.date.year, this.date.month, this.date.day, 1)
        this.$set(this.dateSet, this.nextIndex, data)
      }
      else {
        const data = this.type === 'month' ?
          CM.Date.getCalendarMonth(this.prevMonth.year, this.prevMonth.month) :
          CM.Date.getCalendarWeek(this.date.year, this.date.month, this.date.day, -1)
        this.$set(this.dateSet, this.prevIndex, data)
      }
      
      // 触发日期变动事件
      this.onDateChange() 
    },
    
    // 手动控制滑动组件右滑
    swipe (offset = 1, callback = () => {}) {
      // #ifdef H5
      // 这里必须涉及到源码，否则会无法达到循环滚动的效果
      const swiper = this.$refs.swiper
      const newVal = swiper._normalizeCurrentValue(this.curIndex + offset)
      // 指定动画行为，否则当滑到循环处时，会出现反向滑动的BUG
      swiper.currentChangeSource = 'autoplay'
      // 滑动索引跟进
      swiper.currentSync = newVal
      // 播放滑动动画，第三个参数决定了滑动方向，大于0时右滑，小于0时左滑
      swiper._animateViewport(newVal, '', offset)
      this.swiperIndex = newVal
      // #endif
      
      // #ifdef MP
      this.swiperIndex = (this.swiperIndex + 3 + (offset % 3)) % 3
      // #endif
      
      // #ifdef APP-PLUS
      // APP端无法调用this.$refs.swiper的方法
      if (offset < 0) {
        // 往左滑是正常的
        this.swiperIndex = (this.swiperIndex + 3 + (offset % 3)) % 3
      }
      else {
        // 右滑只能让current不断增加，否则会出现问题
        this.swiperIndex++
      }
      // #endif
      
      clearTimeout(this.swipeTimer)
      this.swipeTimer = setTimeout(() => {
        // 动画过后再触发回调函数，携带当前页面的参数
        callback(this.swiperIndex % 3)
      }, this.aniDuration)
    }
  },
  created () {
    this.init(this.dateInit)
  }
}